#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include <stdio.h>
#include <string.h>
#include "W2564.h"
// W25Q64指令定义
#define W25Q64_CMD_READ_ID2       0x9F  // 读取ID指令
#define W25Q64_CMD_READ_ID        0x90  // 读取ID指令
#define W25Q64_CMD_WRITE_ENABLE   0x06  // 写使能指令
#define W25Q64_CMD_PAGE_PROGRAM   0x02  // 页编程指令
#define W25Q64_CMD_READ_DATA      0x03  // 读取数据指令
#define W25Q64_CMD_SECTOR_ERASE   0x20  // 扇区擦除指令
#define W25Q64_CMD_CHIP_ERASE     0xC7  // 芯片擦除指令
#define W25Q64_CMD_READ_STATUS    0x05  // 读取状态寄存器指令

// SPI配置
#define W25_HOST        SPI2_HOST
#define PIN_NUM_MISO    GPIO_NUM_13
#define PIN_NUM_MOSI    GPIO_NUM_11
#define PIN_NUM_CLK     GPIO_NUM_12
#define PIN_NUM_CS      GPIO_NUM_10

// W25Q64参数
#define SECTOR_SIZE     4096    // 扇区大小
#define PAGE_SIZE       256     // 页大小
#define FLASH_SIZE      0x800000 // 8MB (64Mbit)

static const char *TAG = "W25Q64_TEST";
spi_device_handle_t spi_handle;

// 软件控制CS引脚拉低
static inline void cs_low(void)
{
    gpio_set_level(PIN_NUM_CS, 0);
    // 短暂延时确保电平稳定
    esp_rom_delay_us(1);
}

// 软件控制CS引脚拉高
static inline void cs_high(void)
{
    gpio_set_level(PIN_NUM_CS, 1);
    // 短暂延时确保电平稳定
    esp_rom_delay_us(1);
}


static void w25q64_wait_busy(void) {
    uint8_t status[2] = {0};
    do {
        cs_low();
        uint8_t cmd = W25Q64_CMD_READ_STATUS;
        spi_transaction_t t = {
            .length = 8 * 2,  // 1字节命令 + 1字节数据
            .tx_buffer = &cmd,
            .rx_buffer = status,
        };
        esp_err_t ret = spi_device_transmit(spi_handle, &t);
        assert(ret == ESP_OK);
        cs_high();
    } while (status[1] & 0x01);  // 忙标志位

    esp_rom_delay_us(10);
}

// 发送写使能命令
static void w25q64_write_enable(void) {
    uint8_t cmd = W25Q64_CMD_WRITE_ENABLE;

    cs_low();

    spi_transaction_t t = {
        .length = 8,  // 1字节命令
        .tx_buffer = &cmd,
    };
    esp_err_t ret = spi_device_transmit(spi_handle, &t);
    assert(ret == ESP_OK);

    cs_high();
    esp_rom_delay_us(10);
}

// 读取Flash ID
static void w25q64_read_id2(uint16_t *manufacturer_id, uint16_t *device_id) {
    uint8_t tx_data[4] = {W25Q64_CMD_READ_ID2, 0x00, 0x00, 0x00};
    uint8_t rx_data[6] = {0};
    
    cs_low();
    spi_transaction_t t = {
        .length = 8 * 6, 
        .tx_buffer = tx_data,
        .rx_buffer = rx_data,
    };
    esp_err_t ret = spi_device_transmit(spi_handle, &t);
    assert(ret == ESP_OK);
    
    *manufacturer_id = rx_data[4];
    *device_id = rx_data[5];  
    cs_high();
    esp_rom_delay_us(10);
}

// 读取Flash ID
static void w25q64_read_id(uint16_t *manufacturer_id, uint16_t *device_id) {
    uint8_t tx_data[4] = {W25Q64_CMD_READ_ID, 0x00, 0x00, 0x00};
    uint8_t rx_data[4] = {0};
    
    cs_low();
    spi_transaction_t t = {
        .length = 8 * 4, 
        .tx_buffer = tx_data,
        .rx_buffer = rx_data,
    };
    esp_err_t ret = spi_device_transmit(spi_handle, &t);
    assert(ret == ESP_OK);
    
    *manufacturer_id = rx_data[1];
    *device_id = rx_data[2]<<8 | rx_data[3];  
    cs_high();
    esp_rom_delay_us(10);
}

// 擦除扇区
static void w25q64_erase_sector(uint32_t address) {
    w25q64_write_enable();
    
    cs_low();
    uint8_t cmd[4] = {W25Q64_CMD_SECTOR_ERASE, 
                      (address >> 16) & 0xFF,
                      (address >> 8) & 0xFF,
                      address & 0xFF};
    
    spi_transaction_t t = {
        .length = 8 * 4,  // 4字节命令
        .tx_buffer = cmd,
    };
    esp_err_t ret = spi_device_transmit(spi_handle, &t);
    assert(ret == ESP_OK);

    // for (int i = 0; i < 4; i++)
    // {
    //     ESP_LOGI(TAG, "写入数据 at %d: 0x%02X", i, cmd[i]);
    // }
    
    cs_high();
    w25q64_wait_busy();
}

// 页编程
static void w25q64_page_program(uint32_t address, const uint8_t *data, size_t len) {
    if (len > PAGE_SIZE)    {len = PAGE_SIZE;}
    
    w25q64_write_enable();
    
    cs_low();
    
    uint8_t *tx_data = malloc(4 + len);
    tx_data[0] = W25Q64_CMD_PAGE_PROGRAM;
    tx_data[1] = (address >> 16) & 0xFF;
    tx_data[2] = (address >> 8) & 0xFF;
    tx_data[3] = address & 0xFF;
    memcpy(&tx_data[4], data, len);
    
    // for (int i = 0; i < len; i++) {
    //     ESP_LOGI(TAG, "写入数据 at %d: 0x%02X", i, tx_data[i]);
    // }

    spi_transaction_t t = {
        .length = 8 * (4 + len),  // 命令 + 地址 + 数据
        .tx_buffer = tx_data,
    };
    esp_err_t ret = spi_device_transmit(spi_handle, &t);
    assert(ret == ESP_OK);
    free(tx_data);
    cs_high();
    
    w25q64_wait_busy();
}

// 读取数据
static void w25q64_read_data(uint32_t address, uint8_t *data, size_t len) {
    cs_low();

    // 第一步：只发送命令和地址，不接收数据
    uint8_t cmd[4] = {W25Q64_CMD_READ_DATA,
                     (address >> 16) & 0xFF,
                     (address >> 8) & 0xFF,
                     address & 0xFF};

    //     // 发送命令和地址的事务
    // spi_transaction_t t_data = {
    //     .length = 8 * (4 + len),  // 仅命令 + 地址
    //     .tx_buffer = cmd,
    //     .rx_buffer = data,  // 不接收数据
    // };
    // esp_err_t ret = spi_device_transmit(spi_handle, &t_data);
    // assert(ret == ESP_OK);
    
    // 发送命令和地址的事务
    spi_transaction_t t_cmd = {
        .length = 8 * 4,  // 仅命令 + 地址
        .tx_buffer = cmd,
        .rx_buffer = NULL,  // 不接收数据
    };
    esp_err_t ret = spi_device_transmit(spi_handle, &t_cmd);
    assert(ret == ESP_OK);
    
    // 第二步：只接收数据，不发送数据
    spi_transaction_t t_data = {
        .length = 8 * len,  // 仅数据长度
        .tx_buffer = NULL,  // 不发送数据
        .rx_buffer = data,  // 接收数据到缓冲区
    };
    ret = spi_device_transmit(spi_handle, &t_data);
    assert(ret == ESP_OK);

    cs_high();
    esp_rom_delay_us(10);

    // for (int i = 0; i < len; i++) {
    //     ESP_LOGI(TAG, "读取数据 at %d: 0x%02X", i, ((uint8_t *)t_data.rx_buffer)[i]);
    // }  
}

// 初始化SPI和W25Q64
void spi_flash_init(void) {

    // 使用软件模拟cs
    gpio_config_t w25q64_cs = {
        .pin_bit_mask = (1ULL << PIN_NUM_CS),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,  // 上拉，默认高电平
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&w25q64_cs);
    
    // 初始状态：CS拉高（未选中芯片）
    gpio_set_level(PIN_NUM_CS, 1);
    
    // 配置SPI总线参数结构体
    spi_bus_config_t buscfg = {
        .miso_io_num = PIN_NUM_MISO,      // MISO引脚定义（主机输入，从机输出）
        .mosi_io_num = PIN_NUM_MOSI,      // MOSI引脚定义（主机输出，从机输入）
        .sclk_io_num = PIN_NUM_CLK,       // SCLK引脚定义（时钟信号）
        .quadwp_io_num = -1,              // Quad模式的WP引脚，-1表示不使用
        .quadhd_io_num = -1,              // Quad模式的HD引脚，-1表示不使用
        .max_transfer_sz = 4096,          // 最大传输大小为4096字节
    };
    
    // 初始化SPI总线，使用自动分配的DMA通道
    // W25_HOST：SPI主机号
    // &buscfg：SPI总线配置结构体指针
    // SPI_DMA_CH_AUTO：自动选择DMA通道
   ESP_ERROR_CHECK(spi_bus_initialize(W25_HOST, &buscfg, SPI_DMA_CH_AUTO)); 
    
    // 配置SPI设备接口参数
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 10 * 1000 * 1000,  // SPI时钟频率设置为20MHz
        .mode = 0,                           // SPI模式0（CPOL=0, CPHA=0）
        .spics_io_num = -1,                  // 片选引脚定义
        .queue_size = 5,                     // 事务队列大小，设置为1表示最多缓存1个事务
        .pre_cb = NULL,                      // 事务前回调函数，NULL表示不使用
        .post_cb = NULL,                     // 事务后回调函数，NULL表示不使用
    };
    
    // 将W25Q64设备添加到SPI总线上
    // W25_HOST：SPI主机号
    // &devcfg：设备配置结构体指针
    // &spi_handle：返回的设备句柄，用于后续SPI操作
    ESP_ERROR_CHECK(spi_bus_add_device(W25_HOST, &devcfg, &spi_handle));
    
    // 打印初始化完成的日志信息
    // ESP_LOGI(TAG, "SPI和W25Q64初始化完成");
}

void W25Q64_test(void)
{
    uint16_t manufacturer_id, device_id;
    uint8_t write_data[500];
    uint8_t read_data[260];
    uint32_t test_address = 0x001000;  // 测试地址
    
    // 初始化SPI和Flash
    spi_flash_init();
    
    // 读取ID
    w25q64_read_id2(&manufacturer_id, &device_id);
    // ESP_LOGI(TAG, "2制造商ID: 0x%04X, 设备ID: 0x%04X", manufacturer_id, device_id);

    w25q64_read_id(&manufacturer_id, &device_id);
    // ESP_LOGI(TAG, "1制造商ID: 0x%04X, 设备ID: 0x%04X", manufacturer_id, device_id);
    
    // 准备测试数据
    for (int i = 0; i < 256; i++) {
        write_data[i] = i;
        // ESP_LOGI(TAG, "写入数据 at %d: 0x%02X", i, write_data[i]);
    }

    
    // 擦除扇区
    // ESP_LOGI(TAG, "擦除扇区 0x%06X", test_address);
    w25q64_erase_sector(test_address);
    
    // 写入数据
    // ESP_LOGI(TAG, "写入数据到地址 0x%06X", test_address);
    w25q64_page_program(test_address, write_data, 256);
    // ESP_LOGI(TAG, "写入数据到地址 0x%06X", test_address+256);
    w25q64_page_program(test_address+256, write_data, 256);

    // 读取数据
    // ESP_LOGI(TAG, "从地址 0x%06X 读取数据", test_address);
    memset(read_data, 0, 256);
    w25q64_read_data(test_address, read_data, 256);
    
    // for (int i = 0; i < 256; i++) {
    //     ESP_LOGI(TAG, "读取数据 at %d: 0x%02X", i, read_data[i]);
    // }
     // 验证数据
    bool data_match = true;
    for (int i = 0; i < 256; i++) {
        if (read_data[i] != write_data[i]) {
            ESP_LOGE(TAG, "数据不匹配 at %d: 预期 0x%02X, 实际 0x%02X", i, write_data[i], read_data[i]);
            data_match = false;
            break;
        }
    }
    
    if (data_match) {
        // ESP_LOGI(TAG, "数据验证成功，读写一致！");
    } else {
        // ESP_LOGE(TAG, "数据验证失败，读写不一致！");
    }
    
    // 循环测试
    // while (1) {
    //     w25q64_read_id(&manufacturer_id, &device_id);
    //     ESP_LOGI(TAG, "制造商ID: 0x%04X, 设备ID: 0x%04X", manufacturer_id, device_id);
    //     vTaskDelay(1000 / portTICK_PERIOD_MS);
    // }
}

